<?php
/**
 * Created by PhpStorm.
 * User: Happy
 * Date: 2016/6/16 0016
 * Time: 11:46
 */
//异步任务插件
class sync_taskPlugin extends Plugin
{

    //由于sqlit数据库只能由一个实例进行读写，所以以定时触发网址的方式实现查询数据库，写入数据库等
    /*plugin加载必要的文件，检测sqlit执行类是否存在，不存在的话添加上**/
    public  function  __construct($path='')
    {
        parent::__construct($path);
        $config=$this->config;
        //子组件类
        $plugin_path= $config['plugin_path'];
        if(!class_exists('ModelLit',false)){
           lib('ModelLit');
        }
        $timer_config_file=$plugin_path.'/timer_config.php';
        write_file($config,$timer_config_file);//每次初始化强制覆盖

       /* if(!file_exists($timer_config_file)){
            write_file($config,$timer_config_file);
        }*/
        $filename= $config['sqlit']['filename'];
        $config['sqlit']['filename']=$plugin_path.'/'.$filename;
        ModelLit::set_link( $config['sqlit']['filename'], $config['sqlit']['db_prefix']);//配置数据连接为本地连接,当其它地方也是用sqli时可能引起混淆，请注意
    }

   //测试demo
    public  function  register_shutdown(){
        register_shutdown_function(function()
        {

            $e = error_get_last();
            $insert=array(
                'status'=>'4',
                'result'=>'fatal error',
                'info'=>'进程退出',
                 'error'=>var_export($e,true),
                 'finish_time'=>date('Y-m-d H:i:s')
            );
            ModelLit('log')->insert($insert);
           // $detail=$GLOBALS['detail_info'];
           // $info=   file_get_contents('http://localhost:9092/mobile/index.php?a=b');
            // file_put_contents('a.log','ssdd');
            // file_put_contents('sync.txt',var_export($e,true),FILE_APPEND);
            // file_put_contents('sync.txt','source code:'.$detail,FILE_APPEND);
            //记录错误信息
            print_r($e);
        });
    }
     //要记录闭包的原始位置

     //添加回调函数和参数到队列中，异步执行，$key只是作为标记或者额外说明，不填写自动生成  核心函数
    public  function  sync($callback,$param=array(),$key=''){
   $config=$this->config;
   //存储任务到队列中，防止异步任务过多忙不过来
     $func=   $this->my_closure_dump($callback);
        //查找最后一个}
       $pos= strrpos($func,'}');
       $func= substr($func,0,$pos+1); //函数体字符串
       $param=json_encode($param);//参数转化为字符串
        //获取原始闭包信息
       $detail=ReflectionFunction::export($callback,true);
    //组装成eval可以执行的字符串
        $eval_str=" call_user_func($func,json_decode('$param'));";
      //以上过程大概需要3ms
     //存入数据库中
        $insert=array(
          'function'=>$func,
          'param'=>$param,
          'run_code'=>$eval_str,
           'add_time'=>date('Y-m-d H:i:s'),
           'info'=>$key,
           'detail'=>$detail,
           'url'=>$config['notify_url']
        );

      $flag=  ModelLit('task')->insert($insert);
        //如果lock.txt不存在就启动守护进程
        $plugin_path=$config['plugin_path'];
        if(!file_exists($plugin_path.'/lib/lock.txt')){
            $php_path=$plugin_path.'/lib/timer.php';
            pclose(popen("start php $php_path>>$plugin_path/log.txt&", 'r')); //在timer.php的输出会到log.txt
        }
      return  $flag;
    }

   //显示任务日志
    public  function  show_log(){
      $data_set= ModelLit('log')->limit(100)->order('id desc')->select();
        if(!$data_set){
            echo  'no log item !';
        }
        else{
            print_r($data_set);
        }
        exit();

    }
    //显示任务
    public  function  show_task(){
        $data_set= ModelLit('task')->limit(100)->order('id desc')->select();
        if(!$data_set){
            echo  'no task item !';
        }
        else{
            print_r($data_set);
        }
        exit();
    }
    //清除日志log
    public  function  clear_log(){
        ModelLit('log')->delete();
     echo 'clear all log';
        exit();
    }
    //清除任务
    public  function  clear_task(){

        ModelLit('task')->delete();
        echo 'clear all task';
        exit();
    }


  //创建循环检测 测试程序
    public  function  create_circle_demo(){
        $config=$this->config;
        $plugin_path=$config['plugin_path'];
        if(!file_exists($plugin_path.'/lib/lock.txt')){
            $php_path=  $plugin_path.'/lib/timer.php';
            // $time=time();
            pclose(popen("start php $php_path>>$plugin_path/log.txt&", 'r')); //在timer.php的输出会到log.txt
          // echo time_end($time);
        }
    }


   //获取闭包函数体
    function my_closure_dump(Closure $c) {
        $str = 'function (';
        $r = new ReflectionFunction($c);
        $params = array();
        foreach($r->getParameters() as $p) {
            $s = '';
            if($p->isArray()) {
                $s .= 'array ';
            } else if($p->getClass()) {
                $s .= $p->getClass()->name . ' ';
            }
            if($p->isPassedByReference()){
                $s .= '&';
            }
            $s .= '$' . $p->name;
            if($p->isOptional()) {
                $s .= ' = ' . var_export($p->getDefaultValue(), TRUE);
            }
            $params []= $s;
        }
        $str .= implode(', ', $params);
        $str .= '){' . PHP_EOL;
        $lines = file($r->getFileName());
        for($l = $r->getStartLine(); $l < $r->getEndLine(); $l++) {
            $str .= $lines[$l];
        }
        return $str;
    }

   //模拟执行指定任务
    public  function  run_task(){
         //触发的用户不需要session,所以销毁当前用户的session
        if(isset($_SESSION)){
            session_destroy();
        }
         //注入死机写日志
        register_shutdown_function(function()
        {
            $e = error_get_last();
            if($e['type']==1) { //终极错误才报告 faltal error

                $task_id= $GLOBALS['task_id'];
               $value= ModelLit('task')->where(array('id'=>$task_id))->find();
                $log = array(
                    'status' => '4',
                    'result' => 'fatal error',
                    'error' => var_export($e, true),
                    'finish_time' => date('Y-m-d H:i:s')
                );
                $log=array_merge($value,$log);
                unset($log['id']);//去除原来的taskid
                //写入数据库，删除原来的数据
                ModelLit::begin();

                //在异常中可以获取try 外面的东西变量等
                ModelLit('task')->where(array('id'=>$task_id))->delete();
                ModelLit('log')->insert($log);

                ModelLit::commit();

                //记录错误信息
                print_r($e);
            }
        });


     //设置意外退出守护程序
        $url=urldecode($_GET['url']);
        //同样的url下的任务才能执行，防止执行了其它应用的异步任务
       $tasks= ModelLit('task')->where(array('url'=>$url))->select();
         if($tasks){
              //遍历任务和执行，执行完成的和出错的记录到log并删除
             foreach($tasks as $key=>$value){

                 $task_id=$value['id'];
                 $GLOBALS['task_id']=$task_id;
                 $func=$value['function'];
                 $detail=$value['detail'];
                 $param=json_decode($value['param'],true);
                 $clojure=eval("return $func;"); //返回函数闭包
                 try{
                     $clojure=$clojure?$clojure:function(){};
                     $result= $clojure($param);
                     if(!$result){$result='执行成功';}
                     $log=array(
                         'result'=>$result,
                         'finish_time'=>date('Y-m-d H:i:s'),
                         'status'=>1   //运行成功
                     );
                     $log=array_merge($value,$log);
                     unset($log['id']);//去除原来的taskid
                     //写入数据库，删除原来的数据
                     ModelLit::begin();

                     //在异常中可以获取try 外面的东西变量等
                     ModelLit('task')->where(array('id'=>$task_id))->delete();
                     ModelLit('log')->insert($log);

                     ModelLit::commit();
                 }
                 catch(Exception $ex){ //异常和错误处理
                     $trace= $ex->getTrace();
                     $trace=var_export($trace,true); //输出结果到log.txt
                      $msg=$ex->getMessage();

             file_put_contents('sync.log','a sync error happend ,detail info: '.$msg.$detail,FILE_APPEND);

                     $log=array(
                      'result'=>'执行失败',
                      'error'=>$msg. $trace,
                      'finish_time'=>date('Y-m-d H:i:s'),
                       'status'=>3
                     );
                     $log=array_merge($value,$log);
                     unset($log['id']);//去除原来的taskid
                     //写入数据库，删除原来的数据
                    ModelLit::begin();

                    //在异常中可以获取try 外面的东西变量等
                     ModelLit('task')->where(array('id'=>$task_id))->delete();
                     ModelLit('log')->insert($log);

                    ModelLit::commit();
                     //多次操作一次执行
                 }
             }
         }
       //执行完成直接退出，也可以不退出节省资源一直执行
      $arr=array('exit'=>true);
     exit(json_encode($arr));//结束进程
    }

}